package com.plm.mqshequ.controller;

import com.alibaba.fastjson.JSON;
import com.google.code.kaptcha.Constants;
import com.plm.mqshequ.entity.*;
import com.plm.mqshequ.nosql.MongoPostOps;
import com.plm.mqshequ.nosql.MongoReplyOps;
import com.plm.mqshequ.nosql.RedisKeyOps;
import com.plm.mqshequ.nosql.RedisStringOps;
import com.plm.mqshequ.service.RoleService;
import com.plm.mqshequ.service.UserRoleService;
import com.plm.mqshequ.service.UserService;
import com.plm.mqshequ.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.websocket.server.PathParam;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author Limi
 * @create 2019-01-17 21:58
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private RedisKeyOps keyOps;
    @Autowired
    private RedisStringOps strOps;
    @Autowired
    private MongoPostOps postOps;
    @Autowired
    private MongoReplyOps replyOps;

    @GetMapping("/login")
    public ModelAndView login(){
        ModelAndView mv = new ModelAndView("user/login");
        return mv;
    }

    @GetMapping("/logout")
    public ModelAndView logout() {
        ModelAndView mv = new ModelAndView("redirect:login");
        Object principal = SecurityUtils.getSubject().getPrincipal();
        // 如果已经登录，则跳转到管理首页
        if (principal != null) {
            SecurityUtils.getSubject().logout();
        }
        return mv;
    }

    @GetMapping("/reg")
    public ModelAndView toReg(){
        ModelAndView mv = new ModelAndView("user/reg");
        return mv;
    }

    @GetMapping("/activate")
    public ModelAndView activate(Model model){
        ModelAndView mv = new ModelAndView("user/activate");
        User user = SessionUtils.getUser();
        model.addAttribute("user", user);
        return mv;
    }

    @GetMapping("/forget")
    public ModelAndView forget(){
        ModelAndView mv = new ModelAndView("user/forget");
        return mv;
    }

    @GetMapping("/home")
    public ModelAndView home(Model model){
        ModelAndView mv = new ModelAndView("user/home");
        User user = SessionUtils.getUser();

        Role role = roleService.queryByUId(user.getId());
        user.setRole(role.getRole());
        user.setRoleName(role.getName());

        model.addAttribute("user", user);
        return mv;
    }

    @GetMapping("/center")
    public ModelAndView center(Model model){
        ModelAndView mv = new ModelAndView("user/center");
        User user = SessionUtils.getUser();
        model.addAttribute("user", user);
        return mv;
    }

    @GetMapping("/set")
    public ModelAndView set(Model model){
        ModelAndView mv = new ModelAndView("user/set");
        User user = SessionUtils.getUser();
        model.addAttribute("user", user);
        return mv;
    }

    @GetMapping("/mypost")
    public ModelAndView mypost(Model model){
        ModelAndView mv = new ModelAndView("user/post");
        User user = SessionUtils.getUser();

        List<Post> posts = null;
        try {
            posts = postOps.findByUid(user.getId());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        model.addAttribute("user", user).addAttribute("posts", posts);
        return mv;
    }

    @GetMapping("/message")
    public ModelAndView message(Model model){
        ModelAndView mv = new ModelAndView("user/message");
        User user = SessionUtils.getUser();

        model.addAttribute("user", user);
        return mv;
    }

    @PostMapping("/authEmail")
    public ResultMsg authEmail(@PathParam("email") String email){
        if (null == email || "".equals(email)) {
            return ResultMsg.error("邮箱不能为空");
        }
        try {
            User user = userService.findByEmail(email);
            if (null != user) {
                return ResultMsg.error("该邮箱已被注册");
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }

        return ResultMsg.ok("该邮箱可用");
    }

    @PostMapping("/regHandle")
    public ResultMsg regHandle(@RequestParam Map<String, Object> params){
        String authcode = (String) params.get("authcode");
        if (verifyAuthCode(authcode)) {
            return ResultMsg.error("验证码输入有误");
        }
        User user = JSON.parseObject(JSON.toJSONString(params), User.class);

        //验证邮箱是否被注册
        User user2 = userService.findByEmail(user.getEmail());
        if (null != user2) {
            return ResultMsg.error("该邮箱已被注册");
        }

        String userId = StrUtils.uuid();
        user.setId(userId);
        //邮箱号做盐
        user.setPassword(EncryptUtils.MD5(user.getPassword(), user.getEmail(), 10000));
        user.setStatus("1");//0：被锁定；1正常
        user.setActivate("0");//邮箱默认为未激活
        user.setAvatar("/static/images/avatar/0.jpg");//设置默认头像
        user.setCreateTime(new Date());

        //给用户分配‘社区成员’角色
        UserRole userRole = new UserRole();
        userRole.setId(StrUtils.uuid());
        userRole.setUid(userId);
        userRole.setRid("4eb5a4f73f3a401cbdb717b4455a2e65");//社区成员角色ID

        try {
            userService.add(user);
            userRoleService.add(userRole);//插入到用户角色中间表
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("注册失败，请联系管理员");
        }

        return ResultMsg.ok("注册成功", "user/login");
    }

    @PostMapping("/loginHandle")
    public ResultMsg loginHandle(@RequestParam Map<String, Object> params){
        String authcode = (String) params.get("authcode");
        if (verifyAuthCode(authcode)) {
            return ResultMsg.error("验证码输入有误");
        }
        String account = (String) params.get("email");
        String password = (String) params.get("password");

        String loginErrorCount = account + ":loginErrorCount";
        String loginLockStatus = account + ":loginLockStatus";

        // 记录已验证错误0次,键不存在才会记录值
        strOps.setnx(loginErrorCount, "0");
        Boolean exists = keyOps.exists(loginLockStatus);
        // 如果账户锁时间未失效
        if (exists) {
            return ResultMsg.error("账号或密码错误大于5次，账号已被锁定，" +
                    "为保护您的账号安全，请于明天 "+strOps.get(loginLockStatus)+" 后再登录");
        }
        Integer count = Integer.valueOf(strOps.get(loginErrorCount));
        // 如果锁定账户时间到期，重置错误登录次数
        if (!exists && count>=5) {
            strOps.set(loginErrorCount, "0");
        }

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(account, password);
        try {
            //交给shiro去帮我们验证
            subject.login(token);

            User user = userService.findByEmail(account);
            //将角色信息更新到User中
            Role role = roleService.queryByUId(user.getId());
            user.setRole(role!=null?role.getRole():"");
            user.setRoleName(role!=null?role.getName():"");
            //登录成功后保存用户信息
            SessionUtils.setUser(user);

            //验证成功，错误登录统计归0
            strOps.set(loginErrorCount, "0");

        } catch (UnknownAccountException e) {
            log.error(e.getMessage());
            return getResultMsg(loginLockStatus, loginErrorCount);
        } catch (IncorrectCredentialsException e) {
            log.error(e.getMessage());
            return getResultMsg(loginLockStatus, loginErrorCount);
        } catch (LockedAccountException e) {
            log.error(e.getMessage());
            return ResultMsg.error("账号已被锁定");
        } catch (AuthenticationException e) {
            log.error(e.getMessage());
            return getResultMsg(loginLockStatus, loginErrorCount);
        }

        return ResultMsg.ok("登录成功", "post/index");
    }

    private ResultMsg getResultMsg(String loginLockStatus, String loginErrorCount) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm:ss");
        String nextLoginTime = dtf.format(LocalDateTime.now());
        // 错误次数自增一
        strOps.incr(loginErrorCount);
        // 获取错误次数
        Integer count = Integer.valueOf(strOps.get(loginErrorCount));
        String errorMsg = count < 5 ? "用户名或密码错误，你还有"+(5-count)+"次输入的机会" : "用户名或密码错误";
        if (count == 5) {
            errorMsg = "账号或密码输入错误大于5次，账号已被锁定，为保护您的账号安全，请明天 "+nextLoginTime+" 后再登录";
            // 设置锁定账号时间(秒)
            strOps.setex(loginLockStatus, 24*60*60, nextLoginTime);
        }
        return ResultMsg.error(errorMsg);
    }

    @PostMapping("/forgetHandle")
    public ResultMsg forgetHandle(@RequestParam Map<String, Object> params){
        String authcode = (String) params.get("authcode");
        String email = (String) params.get("email");
        if (verifyAuthCode(authcode)) {
            return ResultMsg.error("验证码输入有误");
        }
        //随机生成6位数字密码
        String newPssword = StrUtils.randomPass();

        User user = null;
        try {
            user = userService.findByEmail(email);

            //检查邮箱是否激活
            if ("0".equals(user.getActivate())) {
                return ResultMsg.error("邮箱未激活，不允许发送邮件");
            }

            //MD5带盐加密
            String md5Password = EncryptUtils.MD5(newPssword, user.getEmail(), 10000);
            user.setPassword(md5Password);
            userService.updateById(user);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }

        //将随机生成的密码通过邮件通知用户
        boolean isSuccess = EmailUtils.sendEmail(email, newPssword, "0");
        if (!isSuccess) {
            return ResultMsg.error("网络出错，请稍后再试");
        }

        if (user != null) {
            SessionUtils.setUser(user);
        }

        return ResultMsg.ok("密码已发送到您邮箱，请登入您的邮箱查看密码", "user/forget");
    }

    /**
     * 校验验证码
     */
    private boolean verifyAuthCode(String authcode) {
        String text = (String) SessionUtils.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
        if (!text.equals(authcode)) {
            return true;
        }
        return false;
    }

    @PostMapping("/sendEmail")
    public ResultMsg sendEmail(@RequestParam Map<String, Object> params){
        String email = (String) params.get("email");
        String activeCode = StrUtils.randomCode();
        boolean isSuccess = EmailUtils.sendEmail(email, activeCode, "1");
        if (!isSuccess) {
            return ResultMsg.error("网络出错，请稍后再试");
        }
        SessionUtils.getSession().setAttribute("activeCode", activeCode);
        return ResultMsg.ok("发送成功");
    }

    @PostMapping("/activateEmail")
    public ResultMsg activateEmail(@RequestParam Map<String, Object> params){
        String activeCode = (String) params.get("activeCode");
        String code = (String) SessionUtils.getSession().getAttribute("activeCode");
        if (!code.equals(activeCode.toLowerCase())) {
            return ResultMsg.error("激活码错误");
        }
        User user = SessionUtils.getUser();
        user.setActivate("1");
        try {
            userService.updateById(user);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }
        SessionUtils.setUser(user);
        return ResultMsg.ok("激活成功");
    }

    @PostMapping("/updateInfo")
    public ResultMsg updateInfo(@RequestParam Map<String, Object> params){
        try {
            User user = JSON.parseObject(JSON.toJSONString(params), User.class);
            User userOld = userService.findById(user.getId());
            //更新数据
            //页面传过来的邮箱和数据库中的邮箱不一致时，校验邮箱
            if (!user.getEmail().equals(userOld.getEmail())) {
                //验证邮箱是否被注册
                User user2 = userService.findByEmail(user.getEmail());
                if (null != user2) {
                    return ResultMsg.error("该邮箱已被注册");
                }
                userOld.setActivate("0");
                userOld.setEmail(user.getEmail());
            }
            userOld.setUsername(user.getUsername());
            userOld.setGender(user.getGender());
            userOld.setCity(user.getCity());
            userOld.setSign(user.getSign());
            //更新到数据库
            userService.updateById(userOld);
            //更新到session
            SessionUtils.setUser(userOld);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }
        return ResultMsg.ok("更新成功");
    }

    @PostMapping("updateAvatar")
    public ResultMsg updateAvatar(@RequestParam Map<String, Object> params){
        String avatar = (String) params.get("avatar");
        User user = SessionUtils.getUser();
        user.setAvatar(avatar);
        try {
            userService.updateById(user);
            SessionUtils.setUser(user);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }
        return ResultMsg.ok("头像设置成功");
    }

    @PostMapping("/updatePass")
    public ResultMsg updatePass(@RequestParam Map<String, Object> params){
        String oldPass = (String) params.get("nowpass");
        User user = SessionUtils.getUser();
        if (!user.getPassword().equals(EncryptUtils.MD5(oldPass, user.getEmail(),10000))) {
            return ResultMsg.error("原密码有误");
        }
        String newPass = (String) params.get("pass");
        String newPassSlat = EncryptUtils.MD5(newPass, user.getEmail(),10000);
        try {
            user.setPassword(newPassSlat);
            userService.updateById(user);
            SessionUtils.setUser(user);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultMsg.error("系统出错，请联系管理员");
        }
        return ResultMsg.ok("密码修改成功");
    }

    @GetMapping("/uid/{uid}")
    public ModelAndView toUserHome(@PathVariable("uid") String uid, Model model){
        ModelAndView mv = new ModelAndView("user/home");
        try {
            User user = userService.findById(uid);

            Role role = roleService.queryByUId(user.getId());
            user.setRole(role.getRole());
            user.setRoleName(role.getName());

            List<Post> posts = postOps.findByUidRecent(uid);
            List<Reply> replys = replyOps.findByUidRecent(uid);

            model.addAttribute("user", user)
                    .addAttribute("posts", posts)
                    .addAttribute("replys", replys);
        } catch (Exception e) {
              log.error(e.getMessage());
        }

        return mv;
    }

    @GetMapping("/jump/{username}")
    public ModelAndView jump(@PathVariable("username") String username, Model model){
        ModelAndView mv = new ModelAndView("user/home");
        try {
            User user = userService.findByUsername(username);

            Role role = roleService.queryByUId(user.getId());
            user.setRole(role.getRole());
            user.setRoleName(role.getName());

            List<Post> posts = postOps.findByUidRecent(user.getId());
            List<Reply> replys = replyOps.findByUidRecent(user.getId());

            model.addAttribute("user", user)
                    .addAttribute("posts", posts)
                    .addAttribute("replys", replys);
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return mv;
    }

}
